using Microsoft.EntityFrameworkCore;
using ProxyServer.EntityFrameworkCore;
using System.Globalization;
using System.Security.Cryptography.X509Certificates;

namespace ProxyServer.Certs;

public class CertificateHelper
{
    public static X509Certificate2? LoadCertificate(string ext, ReadOnlySpan<byte> bytes, string? password = null)
    {
        var cert = ext switch
        {
            ".pfx" => X509CertificateLoader.LoadPkcs12(
                bytes,
                string.IsNullOrWhiteSpace(password) ? null : password,
                X509KeyStorageFlags.Exportable),
            ".crt" => X509CertificateLoader.LoadCertificate(bytes),
            _ => null
        };

        return cert;
    }

    public static X509Certificate2? Load(string fileExt, ReadOnlySpan<byte> fileBytes, string? password)
    {

        var cert = fileExt switch
        {
            ".crt" => X509CertificateLoader.LoadCertificate(fileBytes),
            ".pfx" => X509CertificateLoader.LoadPkcs12(
                fileBytes,
                string.IsNullOrWhiteSpace(password) ? null : password,
                X509KeyStorageFlags.Exportable),
            _ => null
        };


        return cert;
    }
}

public class CertificateInfo(Guid id)
{
    public CertificateInfo() : this(Guid.NewGuid())
    {
    }

    public Guid Id { get; set; } = id;
    public string? Path { get; set; }

    public string? FriendlyName { get; set; }
    public DateTimeOffset? Expires { get; set; }

    public string? IssuerName { get; set; }
    public string? SubjectName { get; set; }

    public string? Password { get; set; }

    public bool Deleted { get; set; }

    public string? Thumbprint { get; set; }

    public DateTimeOffset CreatedTime { get; set; } = DateTimeOffset.UtcNow;
}

public class CertificateManager
{
    private readonly AppDbContext _appDb;
    private readonly ILogger _logger;

    public CertificateManager(AppDbContext appDb, ILogger<CertificateManager> logger)
    {
        _appDb = appDb;
        _logger = logger;
    }

    //public async Task<IEnumerable<X509Certificate2>> GetCertificate2sAsync(CancellationToken cancellationToken)
    //{
    //    var certs = new List<X509Certificate2>();
    //    var certInfos = await _appDb.CertificateInfos.Where(x => x.Deleted == false).ToListAsync();

    //    foreach (var certInfo in certInfos)
    //    {
    //        if (certInfo.Expires <= DateTimeOffset.Now || string.IsNullOrWhiteSpace(certInfo.Path) || File.Exists(certInfo.Path) == false)
    //        {
    //            continue;
    //        }

    //        var fileExt = Path.GetExtension(certInfo.Path);

    //        var cert = fileExt switch
    //        {
    //            ".pfx" => X509CertificateLoader.LoadPkcs12FromFile(
    //                certInfo.Path,
    //                string.IsNullOrWhiteSpace(certInfo.Password) ? null : certInfo.Password,
    //                X509KeyStorageFlags.Exportable),
    //            ".crt" => X509CertificateLoader.LoadCertificateFromFile(certInfo.Path),
    //            _ => null
    //        };

    //        if (cert is null)
    //        {
    //            continue;
    //        }

    //        certs.Add(cert);
    //    }
    //    return certs;
    //}

    public async IAsyncEnumerable<X509Certificate2> GetCertificatesAsync()
    {
        var certInfos = await _appDb.CertificateInfos.Where(x => x.Deleted == false).ToListAsync();

        foreach (var certInfo in certInfos)
        {
            _logger.LogDebug("Loading {Path} for {Thumbprint} before {Expires}", certInfo.Path, certInfo.Thumbprint, certInfo.Expires);

            if (certInfo.Expires <= DateTimeOffset.Now || string.IsNullOrWhiteSpace(certInfo.Path) || File.Exists(certInfo.Path) == false)
            {
                continue;
            }

            var fileExt = Path.GetExtension(certInfo.Path);

            using var ms = new MemoryStream();
            using var fs = new FileStream(certInfo.Path, FileMode.Open, FileAccess.Read, FileShare.Read);
            await fs.CopyToAsync(ms);

            ReadOnlySpan<byte> fileBytes = ms.ToArray();

            var cert = CertificateHelper.LoadCertificate(fileExt, fileBytes, certInfo.Password);

            if (cert is null)
            {
                continue;
            }

            yield return cert;
        }
    }
}